/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.common.serialize.support.dubbo;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;

import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectInputStream;
import com.alibaba.dubbo.common.serialize.support.java.CompactedObjectOutputStream;
import com.alibaba.dubbo.common.utils.ClassHelper;
import com.alibaba.dubbo.common.utils.IOUtils;
import com.alibaba.dubbo.common.utils.ReflectUtils;
import com.alibaba.dubbo.common.utils.StringUtils;

/**
 * Builder.
 * 
 * @author qian.lei
 *
 * @param <T> type.
 */

@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class Builder<T> implements GenericDataFlags
{
	// Must be protected. by qian.lei
	protected static Logger logger = LoggerFactory.getLogger(Builder.class);

	private static final AtomicLong BUILDER_CLASS_COUNTER = new AtomicLong(0);

	private static final String BUILDER_CLASS_NAME = Builder.class.getName();

	private static final Map<Class<?>, Builder<?>> BuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>();
	private static final Map<Class<?>, Builder<?>> nonSerializableBuilderMap = new ConcurrentHashMap<Class<?>, Builder<?>>();

	private static final String FIELD_CONFIG_SUFFIX = ".fc";

	private static final int MAX_FIELD_CONFIG_FILE_SIZE = 16 * 1024;

	private static final Comparator<String> FNC = new Comparator<String>(){
		public int compare(String n1, String n2){ return compareFieldName(n1, n2); }
	};

	private static final Comparator<Field> FC = new Comparator<Field>(){
		public int compare(Field f1, Field f2){ return compareFieldName(f1.getName(), f2.getName()); }
	};

	private static final Comparator<Constructor> CC = new Comparator<Constructor>(){
		public int compare(Constructor o1, Constructor o2){ return o1.getParameterTypes().length - o2.getParameterTypes().length; }
	};

	// class-descriptor mapper
	private static final List<String> mDescList = new ArrayList<String>();

	private static final Map<String, Integer> mDescMap = new ConcurrentHashMap<String, Integer>();

	public static ClassDescriptorMapper DEFAULT_CLASS_DESCRIPTOR_MAPPER = new ClassDescriptorMapper(){
		public String getDescriptor(int index)
		{
			if( index < 0 || index >= mDescList.size() )
				return null;
			return mDescList.get(index);
		}

		public int getDescriptorIndex(String desc)
		{
			Integer ret = mDescMap.get(desc);
			return ret == null ? -1 : ret.intValue();
		}
	};

	protected Builder(){}

	abstract public Class<T> getType();

	public void writeTo(T obj, OutputStream os) throws IOException
	{
		GenericObjectOutput out = new GenericObjectOutput(os);
		writeTo(obj, out);
		out.flushBuffer();
	}

	public T parseFrom(byte[] b) throws IOException
	{
		return parseFrom(new UnsafeByteArrayInputStream(b));
	}

	public T parseFrom(InputStream is) throws IOException
	{
		return parseFrom(new GenericObjectInput(is));
	}

	abstract public void writeTo(T obj, GenericObjectOutput out) throws IOException;

	abstract public T parseFrom(GenericObjectInput in) throws IOException;

	public static <T> Builder<T> register(Class<T> c, boolean isAllowNonSerializable)
    {
        if( c == Object.class || c.isInterface() )
            return (Builder<T>)GenericBuilder;
        if( c == Object[].class )
            return (Builder<T>)GenericArrayBuilder;

        Builder<T> b = (Builder<T>)BuilderMap.get(c);
        if(null != b) return b;
        
        boolean isSerializable = Serializable.class.isAssignableFrom(c);
        if(!isAllowNonSerializable && !isSerializable) {
            throw new IllegalStateException("Serialized class " + c.getName() +
            " must implement java.io.Serializable (dubbo codec setting: isAllowNonSerializable = false)");
        }
        
        b = (Builder<T>)nonSerializableBuilderMap.get(c);
        if(null != b) return b;
        
        b = newBuilder(c);
        if(isSerializable)
            BuilderMap.put(c, b);
        else
            nonSerializableBuilderMap.put(c, b);
        
        return b;
    } 
	
	public static <T> Builder<T> register(Class<T> c)
	{
	    return register(c, false);
	}

	public static <T> void register(Class<T> c, Builder<T> b)
	{
	    if(Serializable.class.isAssignableFrom(c))
	        BuilderMap.put(c, b);
	    else
	        nonSerializableBuilderMap.put(c, b);
	}

	private static <T> Builder<T> newBuilder(Class<T> c)
	{
		if( c.isPrimitive() )
			throw new RuntimeException("Can not create builder for primitive type: " + c);

		if( logger.isInfoEnabled() )
			logger.info("create Builder for class: " + c);

		Builder<?> builder;
		if( c.isArray() )
			builder = newArrayBuilder(c);
		else
			builder = newObjectBuilder(c);
		return (Builder<T>)builder;
	}
	
	private static Builder<?> newArrayBuilder(Class<?> c)
	{
		Class<?> cc = c.getComponentType();
		if( cc.isInterface() )
			return GenericArrayBuilder;

		ClassLoader cl = ClassHelper.getCallerClassLoader(Builder.class);

		String cn = ReflectUtils.getName(c), ccn = ReflectUtils.getName(cc); // get class name as int[][], double[].
		String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();

		int ix = cn.indexOf(']');
		String s1 = cn.substring(0, ix), s2 = cn.substring(ix); // if name='int[][]' then s1='int[', s2='][]'

		StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code.
		StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code.

		cwt.append("if( $1 == null ){ $2.write0(OBJECT_NULL); return; }");
		cwt.append(cn).append(" v = (").append(cn).append(")$1; int len = v.length; $2.write0(OBJECT_VALUES); $2.writeUInt(len); for(int i=0;i<len;i++){ ");

		cpf.append("byte b = $1.read0(); if( b == OBJECT_NULL ) return null; if( b != OBJECT_VALUES ) throw new java.io.IOException(\"Input format error, expect OBJECT_NULL|OBJECT_VALUES, get \" + b + \".\");");
		cpf.append("int len = $1.readUInt(); if( len == 0 ) return new ").append(s1).append('0').append(s2).append("; ");
		cpf.append(cn).append(" ret = new ").append(s1).append("len").append(s2).append("; for(int i=0;i<len;i++){ ");

		Builder<?> builder = null;
		if( cc.isPrimitive() )
		{
			if( cc == boolean.class )
			{
				cwt.append("$2.writeBool(v[i]);");
				cpf.append("ret[i] = $1.readBool();");
			}
			else if( cc == byte.class )
			{
				cwt.append("$2.writeByte(v[i]);");
				cpf.append("ret[i] = $1.readByte();");
			}
			else if( cc == char.class )
			{
				cwt.append("$2.writeShort((short)v[i]);");
				cpf.append("ret[i] = (char)$1.readShort();");
			}
			else if( cc == short.class )
			{
				cwt.append("$2.writeShort(v[i]);");
				cpf.append("ret[i] = $1.readShort();");
			}
			else if( cc == int.class )
			{
				cwt.append("$2.writeInt(v[i]);");
				cpf.append("ret[i] = $1.readInt();");
			}
			else if( cc == long.class )
			{
				cwt.append("$2.writeLong(v[i]);");
				cpf.append("ret[i] = $1.readLong();");
			}
			else if( cc == float.class )
			{
				cwt.append("$2.writeFloat(v[i]);");
				cpf.append("ret[i] = $1.readFloat();");
			}
			else if( cc == double.class )
			{
				cwt.append("$2.writeDouble(v[i]);");
				cpf.append("ret[i] = $1.readDouble();");
			}
		}
		else
		{
			builder = register(cc);

			cwt.append("builder.writeTo(v[i], $2);");
			cpf.append("ret[i] = (").append(ccn).append(")builder.parseFrom($1);");
		}
		cwt.append(" } }");
		cpf.append(" } return ret; }");

		ClassGenerator cg = ClassGenerator.newInstance(cl);
		cg.setClassName(bcn);
		cg.setSuperClass(Builder.class);
		cg.addDefaultConstructor();
		if( builder != null )
			cg.addField("public static " + BUILDER_CLASS_NAME + " builder;");
		cg.addMethod("public Class getType(){ return " + cn + ".class; }");
		cg.addMethod(cwt.toString());
		cg.addMethod(cpf.toString());
		try
		{
			Class<?> wc = cg.toClass();
			// set static field.
			if( builder != null )
				wc.getField("builder").set(null, builder);
			return (Builder<?>)wc.newInstance();
		}
		catch(RuntimeException e)
		{
			throw e;
		}
		catch(Throwable e)
		{
			throw new RuntimeException(e.getMessage());
		}
		finally
		{
			cg.release();
		}
	}

	private static Builder<?> newObjectBuilder(final Class<?> c)
	{
		if( c.isEnum() )
			return newEnumBuilder(c);

		if( c.isAnonymousClass() )
			throw new RuntimeException("Can not instantiation anonymous class: " + c);

		if( c.getEnclosingClass() != null && !Modifier.isStatic(c.getModifiers()) )
			throw new RuntimeException("Can not instantiation inner and non-static class: " + c);

		if( Throwable.class.isAssignableFrom(c) )
			return SerializableBuilder;

		ClassLoader cl = ClassHelper.getCallerClassLoader(Builder.class);
	
		// is same package.
		boolean isp;
		String cn = c.getName(), bcn;
		if( c.getClassLoader() == null ) // is system class. if( cn.startsWith("java.") || cn.startsWith("javax.") || cn.startsWith("sun.") )
		{
			isp = false;
			bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
		}
		else
		{
			isp = true;
			bcn = cn + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();
		}

		// is Collection, is Map, is Serializable.
		boolean isc = Collection.class.isAssignableFrom(c);
		boolean ism = !isc && Map.class.isAssignableFrom(c);
		boolean iss = !( isc || ism ) && Serializable.class.isAssignableFrom(c);

		// deal with fields.
		String[] fns = null; // fix-order fields names
		InputStream is = c.getResourceAsStream(c.getSimpleName() + FIELD_CONFIG_SUFFIX); // load field-config file.
		if( is != null )
		{
			try
			{
				int len = is.available();
				if( len > 0 )
				{
					if( len > MAX_FIELD_CONFIG_FILE_SIZE )
						throw new RuntimeException("Load [" + c.getName() + "] field-config file error: File-size too larger");

					String[] lines = IOUtils.readLines(is);
					if( lines != null && lines.length > 0 )
					{
						List<String> list = new ArrayList<String>();
						for(int i=0;i<lines.length;i++)
						{
							fns = lines[i].split(",");
							Arrays.sort(fns, FNC);
							for(int j=0;j<fns.length;j++)
								list.add(fns[j]);
						}
						fns = list.toArray(new String[0]);
					}
				}
			}
			catch(IOException e)
			{
				throw new RuntimeException("Load [" + c.getName() + "] field-config file error: " + e.getMessage() );
			}
			finally
			{
				try{ is.close(); }
				catch(IOException e){}
			}
		}

		Field f, fs[];
		if( fns != null )
		{
			fs = new Field[fns.length];
			for(int i=0;i<fns.length;i++)
			{
				String fn = fns[i];
				try
				{
					f = c.getDeclaredField(fn);
					int mod = f.getModifiers();
					if( Modifier.isStatic(mod) || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod)) )
						throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is static/final field.");
					if( Modifier.isTransient(mod) )
					{
						if( iss )
							return SerializableBuilder;
						throw new RuntimeException("Field [" + c.getName() + "." + fn + "] is transient field.");
					}
					f.setAccessible(true);
					fs[i] = f;
				}
				catch(SecurityException e)
				{
					throw new RuntimeException(e.getMessage());
				}
				catch(NoSuchFieldException e)
				{
					throw new RuntimeException("Field [" + c.getName() + "." + fn + "] not found.");
				}
			}
		}
		else
		{
			Class<?> t = c;
			List<Field> fl = new ArrayList<Field>();
			do
			{
				fs = t.getDeclaredFields();
				for( Field tf : fs )
				{
					int mod = tf.getModifiers();
                    if (Modifier.isStatic(mod)
                            || (serializeIgnoreFinalModifier(c) && Modifier.isFinal(mod))
                            || tf.getName().equals("this$0") // skip static or inner-class's 'this$0' field.
                            || ! Modifier.isPublic(tf.getType().getModifiers()) ) //skip private inner-class field
						continue;
					if( Modifier.isTransient(mod) )
					{
						if( iss )
							return SerializableBuilder;
						continue;
					}
					tf.setAccessible(true);
					fl.add(tf);
				}
				t = t.getSuperclass();
			}
			while( t != Object.class );

			fs = fl.toArray(new Field[0]);
			if( fs.length > 1 )
				Arrays.sort(fs, FC);
		}

		// deal with constructors.
		Constructor<?>[] cs = c.getDeclaredConstructors();
		if( cs.length == 0 )
		{
			Class<?> t = c;
			do
			{
				t = t.getSuperclass();
				if( t == null )
					throw new RuntimeException("Can not found Constructor?");
				cs = c.getDeclaredConstructors();
			}
			while( cs.length == 0 );
		}
		if( cs.length > 1 )
			Arrays.sort(cs, CC);

		// writeObject code.
		StringBuilder cwf = new StringBuilder("protected void writeObject(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{");
		cwf.append(cn).append(" v = (").append(cn).append(")$1; ");
		cwf.append("$2.writeInt(fields.length);");

		// readObject code.
		StringBuilder crf = new StringBuilder("protected void readObject(Object ret, ").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{");
		crf.append("int fc = $2.readInt();");
		crf.append("if( fc != ").append(fs.length).append(" ) throw new IllegalStateException(\"Deserialize Class [").append(cn).append("], field count not matched. Expect ").append(fs.length).append(" but get \" + fc +\".\");");
		crf.append(cn).append(" ret = (").append(cn).append(")$1;");

		// newInstance code.
		StringBuilder cni = new StringBuilder("protected Object newInstance(").append(GenericObjectInput.class.getName()).append(" in){ return ");
		Constructor<?> con = cs[0];
		int mod = con.getModifiers();
		boolean dn = Modifier.isPublic(mod) || ( isp && !Modifier.isPrivate(mod) );
		if( dn )
		{
			cni.append("new ").append(cn).append("(");
		}
		else
		{
			con.setAccessible(true);
			cni.append("constructor.newInstance(new Object[]{");
		}
		Class<?>[] pts = con.getParameterTypes();
		for(int i=0;i<pts.length;i++)
		{
			if( i > 0 )
				cni.append(',');
			cni.append(defaultArg(pts[i]));
		}
		if( !dn )
			cni.append("}"); // close object array.
		cni.append("); }");

		// get bean-style property metadata.
		Map<String, PropertyMetadata> pms = propertyMetadatas(c);
		List<Builder<?>> builders = new ArrayList<Builder<?>>(fs.length);
		String fn, ftn; // field name, field type name.
		Class<?> ft; // field type.
		boolean da; // direct access.
		PropertyMetadata pm;
		for(int i=0;i<fs.length;i++)
		{
			f = fs[i];
			fn = f.getName();
			ft = f.getType();
			ftn = ReflectUtils.getName(ft);
			da = isp && ( f.getDeclaringClass() == c ) && ( Modifier.isPrivate(f.getModifiers()) == false );
			if( da )
			{
				pm = null;
			}
			else
			{
				pm = pms.get(fn);
				if( pm != null && ( pm.type != ft || pm.setter == null || pm.getter == null ) )
					pm = null;
			}

			crf.append("if( fc == ").append(i).append(" ) return;");
			if( ft.isPrimitive() )
			{
				if( ft == boolean.class )
				{
					if( da )
					{
						cwf.append("$2.writeBool(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readBool();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeBool(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readBool());");
					}
					else
					{
						cwf.append("$2.writeBool(((Boolean)fields[").append(i).append("].get($1)).booleanValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readBool());");
					}
				}
				else if( ft == byte.class )
				{
					if( da )
					{
						cwf.append("$2.writeByte(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readByte();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeByte(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readByte());");
					}
					else
					{
						cwf.append("$2.writeByte(((Byte)fields[").append(i).append("].get($1)).byteValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readByte());");
					}
				}
				else if( ft == char.class )
				{
					if( da )
					{
						cwf.append("$2.writeShort((short)v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = (char)$2.readShort();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeShort((short)v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("((char)$2.readShort());");
					}
					else
					{
						cwf.append("$2.writeShort((short)((Character)fields[").append(i).append("].get($1)).charValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)((char)$2.readShort()));");
					}
				}
				else if( ft == short.class )
				{
					if( da )
					{
						cwf.append("$2.writeShort(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readShort();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeShort(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readShort());");
					}
					else
					{
						cwf.append("$2.writeShort(((Short)fields[").append(i).append("].get($1)).shortValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readShort());");
					}
				}
				else if( ft == int.class )
				{
					if( da )
					{
						cwf.append("$2.writeInt(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readInt();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeInt(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readInt());");
					}
					else
					{
						cwf.append("$2.writeInt(((Integer)fields[").append(i).append("].get($1)).intValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readInt());");
					}
				}
				else if( ft == long.class )
				{
					if( da )
					{
						cwf.append("$2.writeLong(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readLong();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeLong(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readLong());");
					}
					else
					{
						cwf.append("$2.writeLong(((Long)fields[").append(i).append("].get($1)).longValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readLong());");
					}
				}
				else if( ft == float.class )
				{
					if( da )
					{
						cwf.append("$2.writeFloat(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readFloat();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeFloat(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readFloat());");
					}
					else
					{
						cwf.append("$2.writeFloat(((Float)fields[").append(i).append("].get($1)).floatValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readFloat());");
					}
				}
				else if( ft == double.class )
				{
					if( da )
					{
						cwf.append("$2.writeDouble(v.").append(fn).append(");");
						crf.append("ret.").append(fn).append(" = $2.readDouble();");
					}
					else if( pm != null )
					{
						cwf.append("$2.writeDouble(v.").append(pm.getter).append("());");
						crf.append("ret.").append(pm.setter).append("($2.readDouble());");
					}
					else
					{
						cwf.append("$2.writeDouble(((Double)fields[").append(i).append("].get($1)).doubleValue());");
						crf.append("fields[").append(i).append("].set(ret, ($w)$2.readDouble());");
					}
				}
			}
			else if( ft == c )
			{
				if( da )
				{
					cwf.append("this.writeTo(v.").append(fn).append(", $2);");
					crf.append("ret.").append(fn).append(" = (").append(ftn).append(")this.parseFrom($2);");
				}
				else if( pm != null )
				{
					cwf.append("this.writeTo(v.").append(pm.getter).append("(), $2);");
					crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")this.parseFrom($2));");
				}
				else
				{
					cwf.append("this.writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);");
					crf.append("fields[").append(i).append("].set(ret, this.parseFrom($2));");
				}
			}
			else
			{
				int bc = builders.size();
				builders.add( register(ft) );

				if( da )
				{
					cwf.append("builders[").append(bc).append("].writeTo(v.").append(fn).append(", $2);");
					crf.append("ret.").append(fn).append(" = (").append(ftn).append(")builders[").append(bc).append("].parseFrom($2);");
				}
				else if( pm != null )
				{
					cwf.append("builders[").append(bc).append("].writeTo(v.").append(pm.getter).append("(), $2);");
					crf.append("ret.").append(pm.setter).append("((").append(ftn).append(")builders[").append(bc).append("].parseFrom($2));");
				}
				else
				{
					cwf.append("builders[").append(bc).append("].writeTo((").append(ftn).append(")fields[").append(i).append("].get($1), $2);");
					crf.append("fields[").append(i).append("].set(ret, builders[").append(bc).append("].parseFrom($2));");
				}
			}
		}

		// skip any fields.
		crf.append("for(int i=").append(fs.length).append(";i<fc;i++) $2.skipAny();");

		// collection or map
		if( isc )
		{
			cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.iterator();it.hasNext();){ $2.writeObject(it.next()); }");
			crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.add($2.readObject());");
		}
		else if( ism )
		{
			cwf.append("$2.writeInt(v.size()); for(java.util.Iterator it=v.entrySet().iterator();it.hasNext();){ java.util.Map.Entry entry = (java.util.Map.Entry)it.next(); $2.writeObject(entry.getKey()); $2.writeObject(entry.getValue()); }");
			crf.append("int len = $2.readInt(); for(int i=0;i<len;i++) ret.put($2.readObject(), $2.readObject());");
		}
		cwf.append(" }");
		crf.append(" }");

		ClassGenerator cg = ClassGenerator.newInstance(cl);
		cg.setClassName(bcn);
		cg.setSuperClass(AbstractObjectBuilder.class);
		cg.addDefaultConstructor();
		cg.addField("public static java.lang.reflect.Field[] fields;");
		cg.addField("public static " + BUILDER_CLASS_NAME + "[] builders;");
		if( !dn )
			cg.addField("public static java.lang.reflect.Constructor constructor;");
		cg.addMethod("public Class getType(){ return " + cn + ".class; }");
		cg.addMethod(cwf.toString());
		cg.addMethod(crf.toString());
		cg.addMethod(cni.toString());
		try
		{
			Class<?> wc = cg.toClass();
			// set static field
			wc.getField("fields").set(null, fs);
			wc.getField("builders").set(null, builders.toArray(new Builder<?>[0]));
			if( !dn )
				wc.getField("constructor").set(null, con);
			return (Builder<?>)wc.newInstance();
		}
		catch(RuntimeException e)
		{
			throw e;
		}
		catch(Throwable e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		finally
		{
			cg.release();
		}
	}

	private static Builder<?> newEnumBuilder(Class<?> c)
	{
		ClassLoader cl = ClassHelper.getCallerClassLoader(Builder.class);
		
		String cn = c.getName();
		String bcn = BUILDER_CLASS_NAME + "$bc" + BUILDER_CLASS_COUNTER.getAndIncrement();

		StringBuilder cwt = new StringBuilder("public void writeTo(Object obj, ").append(GenericObjectOutput.class.getName()).append(" out) throws java.io.IOException{"); // writeTo code.
		cwt.append(cn).append(" v = (").append(cn).append(")$1;");
		cwt.append("if( $1 == null ){ $2.writeUTF(null); }else{ $2.writeUTF(v.name()); } }");

		StringBuilder cpf = new StringBuilder("public Object parseFrom(").append(GenericObjectInput.class.getName()).append(" in) throws java.io.IOException{"); // parseFrom code.
		cpf.append("String name = $1.readUTF(); if( name == null ) return null; return (").append(cn).append(")Enum.valueOf(").append(cn).append(".class, name); }");

		ClassGenerator cg = ClassGenerator.newInstance(cl);
		cg.setClassName(bcn);
		cg.setSuperClass(Builder.class);
		cg.addDefaultConstructor();
		cg.addMethod("public Class getType(){ return " + cn + ".class; }");
		cg.addMethod(cwt.toString());
		cg.addMethod(cpf.toString());
		try
		{
			Class<?> wc = cg.toClass();
			return (Builder<?>)wc.newInstance();
		}
		catch(RuntimeException e)
		{
			throw e;
		}
		catch(Throwable e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		finally
		{
			cg.release();
		}
	}

	private static Map<String, PropertyMetadata> propertyMetadatas(Class<?> c)
	{
		Map<String, Method> mm = new HashMap<String, Method>(); // method map.
		Map<String, PropertyMetadata> ret = new HashMap<String, PropertyMetadata>(); // property metadata map.

		// All public method.
		for( Method m : c.getMethods() )
		{
			if( m.getDeclaringClass() == Object.class ) // Ignore Object's method.
				continue;
			mm.put(ReflectUtils.getDesc(m), m);
		}

		Matcher matcher;
		for( Map.Entry<String,Method> entry : mm.entrySet() )
		{
			String desc = entry.getKey();
			Method method = entry.getValue();
			if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() ||
					( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(desc) ).matches() )
			{
				String pn = propertyName(matcher.group(1));
				Class<?> pt = method.getReturnType();
				PropertyMetadata pm = ret.get(pn);
				if( pm == null )
				{
					pm = new PropertyMetadata();
					pm.type = pt;
					ret.put(pn, pm);
				}
				else
				{
					if( pm.type != pt )
						continue;
				}
				pm.getter = method.getName();
			}
			else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(desc) ).matches() )
			{
				String pn = propertyName(matcher.group(1));
				Class<?> pt = method.getParameterTypes()[0];
				PropertyMetadata pm = ret.get(pn);
				if( pm == null )
				{
					pm = new PropertyMetadata();
					pm.type = pt;
					ret.put(pn, pm);
				}
				else
				{
					if( pm.type != pt )
						continue;
				}
				pm.setter = method.getName();
			}
		}
		return ret;
	}

	private static String propertyName(String s)
	{
		return s.length() == 1 || Character.isLowerCase(s.charAt(1)) ? Character.toLowerCase(s.charAt(0)) + s.substring(1) : s;
	}
	
	private static boolean serializeIgnoreFinalModifier(Class cl)
    {
//	    if (cl.isAssignableFrom(BigInteger.class)) return false;
//	    for performance
//	    if (cl.getName().startsWith("java")) return true;
//	    if (cl.getName().startsWith("javax")) return true;
	    
	    return false;
    }
	
	@SuppressWarnings("unused")
    private static boolean isPrimitiveOrPrimitiveArray1(Class<?> cl)
    {
        if (cl.isPrimitive()){
            return true;
        } else {
            Class clazz = cl.getClass().getComponentType();
            if (clazz!=null && clazz.isPrimitive()){
                return true;
            }
        } 
        return false;
    }

	private static String defaultArg(Class<?> cl)
	{
	    if( boolean.class == cl ) return "false";
	    if( int.class == cl ) return "0";
	    if( long.class == cl ) return "0l";
	    if( double.class == cl ) return "(double)0";
	    if( float.class == cl ) return "(float)0";
	    if( short.class == cl ) return "(short)0";
	    if( char.class == cl ) return "(char)0";
	    if( byte.class == cl ) return "(byte)0";
	    if( byte[].class == cl ) return "new byte[]{0}";
	    if( !cl.isPrimitive() ) return "null";
	    throw new UnsupportedOperationException();
	}

	private static int compareFieldName(String n1, String n2)
	{
		int l = Math.min(n1.length(), n2.length());
		for(int i=0;i<l;i++)
		{
			int t = n1.charAt(i) - n2.charAt(i);
			if( t != 0 )
				return t;
		}
		return n1.length() - n2.length();
	}

	private static void addDesc(Class<?> c)
	{
		String desc = ReflectUtils.getDesc(c);
		int index = mDescList.size();
		mDescList.add(desc);
		mDescMap.put(desc, index);
	}

	static class PropertyMetadata
	{
		Class<?> type;
		String setter, getter;
	}

	public static abstract class AbstractObjectBuilder<T> extends Builder<T>
	{
		abstract public Class<T> getType();

		public void writeTo(T obj, GenericObjectOutput out) throws IOException
		{
			if( obj == null )
			{
				out.write0(OBJECT_NULL);
			}
			else
			{
				int ref = out.getRef(obj);
				if( ref < 0 )
				{
					out.addRef(obj);
					out.write0(OBJECT);
					writeObject(obj, out);
				}
				else
				{
					out.write0(OBJECT_REF);
					out.writeUInt(ref);
				}
			}
		}

		public T parseFrom(GenericObjectInput in) throws IOException
		{
			byte b = in.read0();
			switch( b )
			{
				case OBJECT:
				{
					T ret = newInstance(in);
					in.addRef(ret);
					readObject(ret, in);
					return ret;
				}
				case OBJECT_REF:
					return (T)in.getRef(in.readUInt());
				case OBJECT_NULL:
					return null;
				default:
					throw new IOException("Input format error, expect OBJECT|OBJECT_REF|OBJECT_NULL, get " + b);
			}
		}

		abstract protected void writeObject(T obj, GenericObjectOutput out) throws IOException;

		abstract protected T newInstance(GenericObjectInput in) throws IOException;

		abstract protected void readObject(T ret, GenericObjectInput in) throws IOException;
	}

	static final Builder<Object> GenericBuilder = new Builder<Object>(){
		@Override
		public Class<Object> getType(){ return Object.class; }
		@Override
		public void writeTo(Object obj, GenericObjectOutput out) throws IOException{ out.writeObject(obj); }
		@Override
		public Object parseFrom(GenericObjectInput in) throws IOException{ return in.readObject(); }
	};

	static final Builder<Object[]> GenericArrayBuilder = new AbstractObjectBuilder<Object[]>(){
		@Override
		public Class<Object[]> getType(){ return Object[].class; }
		@Override
		protected Object[] newInstance(GenericObjectInput in) throws IOException
		{
			return new Object[in.readUInt()];
		}
		@Override
		protected void readObject(Object[] ret, GenericObjectInput in) throws IOException
		{
			for(int i=0;i<ret.length;i++)
				ret[i] = in.readObject();
		}
		@Override
		protected void writeObject(Object[] obj, GenericObjectOutput out) throws IOException
		{
			out.writeUInt(obj.length);
			for( Object item : obj )
				out.writeObject(item);
		}
	};

	static final Builder<Serializable> SerializableBuilder = new Builder<Serializable>(){
		@Override
		public Class<Serializable> getType()
		{
			return Serializable.class;
		}
		@Override
		public void writeTo(Serializable obj, GenericObjectOutput out) throws IOException
		{
			if( obj == null )
			{
				out.write0(OBJECT_NULL);
			}
			else
			{
				out.write0(OBJECT_STREAM);
				UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
				CompactedObjectOutputStream oos = new CompactedObjectOutputStream(bos);
				oos.writeObject(obj);
				oos.flush();
				bos.close();
				byte[] b = bos.toByteArray();
				out.writeUInt(b.length);
				out.write0(b, 0, b.length);
			}
		}
		@Override
		public Serializable parseFrom(GenericObjectInput in) throws IOException
		{
			byte b = in.read0();
			if( b == OBJECT_NULL )
				return null;
			if( b != OBJECT_STREAM )
				throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_STREAM, get " + b + ".");

			UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(in.read0(in.readUInt()));
			CompactedObjectInputStream ois = new CompactedObjectInputStream(bis);
			try{ return (Serializable)ois.readObject(); }
			catch(ClassNotFoundException e){ throw new IOException(StringUtils.toString(e)); }
		}
	};

	static
	{
		addDesc(boolean[].class);
		addDesc(byte[].class);
		addDesc(char[].class);
		addDesc(short[].class);
		addDesc(int[].class);
		addDesc(long[].class);
		addDesc(float[].class);
		addDesc(double[].class);

		addDesc(Boolean.class);
		addDesc(Byte.class);
		addDesc(Character.class);
		addDesc(Short.class);
		addDesc(Integer.class);
		addDesc(Long.class);
		addDesc(Float.class);
		addDesc(Double.class);

		addDesc(String.class);
		addDesc(String[].class);

		addDesc(ArrayList.class);
		addDesc(HashMap.class);
		addDesc(HashSet.class);
		addDesc(Date.class);
		addDesc(java.sql.Date.class);
		addDesc(java.sql.Time.class);
		addDesc(java.sql.Timestamp.class);
		addDesc(java.util.LinkedList.class);
		addDesc(java.util.LinkedHashMap.class);
		addDesc(java.util.LinkedHashSet.class);

		register(byte[].class, new Builder<byte[]>(){
			@Override
			public Class<byte[]> getType(){ return byte[].class; }
			@Override
			public void writeTo(byte[] obj, GenericObjectOutput out) throws IOException{ out.writeBytes(obj); }
			@Override
			public byte[] parseFrom(GenericObjectInput in) throws IOException{ return in.readBytes(); }
		});
		register(Boolean.class, new Builder<Boolean>(){
			@Override
			public Class<Boolean> getType(){ return Boolean.class; }
			@Override
			public void writeTo(Boolean obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
					out.write0(VARINT_N1);
				else if( obj.booleanValue() )
					out.write0(VARINT_1);
				else
					out.write0(VARINT_0);
			}
			@Override
			public Boolean parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				switch( b )
				{
					case VARINT_N1: return null;
					case VARINT_0: return Boolean.FALSE;
					case VARINT_1: return Boolean.TRUE;
					default: throw new IOException("Input format error, expect VARINT_N1|VARINT_0|VARINT_1, get " + b + ".");
				}
			}
		});
		register(Byte.class, new Builder<Byte>(){
			@Override
			public Class<Byte> getType(){ return Byte.class; }
			@Override
			public void writeTo(Byte obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeByte(obj.byteValue());
				}
			}
			@Override
			public Byte parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return Byte.valueOf(in.readByte());
			}
		});
		register(Character.class, new Builder<Character>(){
			@Override
			public Class<Character> getType(){ return Character.class; }
			@Override
			public void writeTo(Character obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeShort((short)obj.charValue());
				}
			}
			@Override
			public Character parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return Character.valueOf((char)in.readShort());
			}
		});
		register(Short.class, new Builder<Short>(){
			@Override
			public Class<Short> getType(){ return Short.class; }
			@Override
			public void writeTo(Short obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeShort(obj.shortValue());
				}
			}
			@Override
			public Short parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return Short.valueOf(in.readShort());
			}
		});
		register(Integer.class, new Builder<Integer>(){
			@Override
			public Class<Integer> getType(){ return Integer.class; }
			@Override
			public void writeTo(Integer obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeInt(obj.intValue());
				}
			}
			@Override
			public Integer parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return Integer.valueOf(in.readInt());
			}
		});
		register(Long.class, new Builder<Long>(){
			@Override
			public Class<Long> getType(){ return Long.class; }
			@Override
			public void writeTo(Long obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeLong(obj.longValue());
				}
			}
			@Override
			public Long parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return Long.valueOf(in.readLong());
			}
		});
		register(Float.class, new Builder<Float>(){
			@Override
			public Class<Float> getType(){ return Float.class; }
			@Override
			public void writeTo(Float obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeFloat(obj.floatValue());
				}
			}
			@Override
			public Float parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return new Float(in.readFloat());
			}
		});
		register(Double.class, new Builder<Double>(){
			@Override
			public Class<Double> getType(){ return Double.class; }
			@Override
			public void writeTo(Double obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeDouble(obj.doubleValue());
				}
			}
			@Override
			public Double parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return new Double(in.readDouble());
			}
		});
		register(String.class, new Builder<String>(){
			@Override
			public Class<String> getType(){ return String.class; }
			@Override
			public String parseFrom(GenericObjectInput in) throws IOException{ return in.readUTF(); }
			@Override
			public void writeTo(String obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj); }
		});
		register(StringBuilder.class, new Builder<StringBuilder>(){
			@Override
			public Class<StringBuilder> getType(){ return StringBuilder.class; }
			@Override
			public StringBuilder parseFrom(GenericObjectInput in) throws IOException{ return new StringBuilder(in.readUTF()); }
			@Override
			public void writeTo(StringBuilder obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
		});
		register(StringBuffer.class, new Builder<StringBuffer>(){
			@Override
			public Class<StringBuffer> getType(){ return StringBuffer.class; }
			@Override
			public StringBuffer parseFrom(GenericObjectInput in) throws IOException{ return new StringBuffer(in.readUTF()); }
			@Override
			public void writeTo(StringBuffer obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
		});

		// java.util
		register(ArrayList.class, new Builder<ArrayList>(){
			@Override
			public Class<ArrayList> getType(){ return ArrayList.class; }
			@Override
			public void writeTo(ArrayList obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUES);
					out.writeUInt(obj.size());
					for( Object item : obj )
						out.writeObject(item);
				}
			}
			@Override
			public ArrayList parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUES )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");

				int len = in.readUInt();
				ArrayList ret = new ArrayList(len);
				for(int i=0;i<len;i++)
					ret.add(in.readObject());
				return ret;
			}
		});
		register(HashMap.class, new Builder<HashMap>(){
			@Override
			public Class<HashMap> getType(){ return HashMap.class; }
			@Override
			public void writeTo(HashMap obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_MAP);
					out.writeUInt(obj.size());
					for( Map.Entry entry : (Set<Map.Entry>)obj.entrySet() )
					{
						out.writeObject(entry.getKey());
						out.writeObject(entry.getValue());
					}
				}
			}
			@Override
			public HashMap parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_MAP )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_MAP, get " + b + ".");

				int len = in.readUInt();
				HashMap ret = new HashMap(len);
				for(int i=0;i<len;i++)
					ret.put(in.readObject(), in.readObject());
				return ret;
			}
		});
		register(HashSet.class, new Builder<HashSet>(){
			@Override
			public Class<HashSet> getType(){ return HashSet.class; }
			@Override
			public void writeTo(HashSet obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUES);
					out.writeUInt(obj.size());
					for( Object item : obj )
						out.writeObject(item);
				}
			}
			@Override
			public HashSet parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUES )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");

				int len = in.readUInt();
				HashSet ret = new HashSet(len);
				for(int i=0;i<len;i++)
					ret.add(in.readObject());
				return ret;
			}
		});

		register(Date.class, new Builder<Date>(){
			@Override
			public Class<Date> getType(){ return Date.class; }
			@Override
			public void writeTo(Date obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeLong(obj.getTime());
				}
			}
			@Override
			public Date parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return new Date(in.readLong());
			}
		});

		// java.sql
		register(java.sql.Date.class, new Builder<java.sql.Date>(){
			@Override
			public Class<java.sql.Date> getType(){ return java.sql.Date.class; }
			@Override
			public void writeTo(java.sql.Date obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeLong(obj.getTime());
				}
			}
			@Override
			public java.sql.Date parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return new java.sql.Date(in.readLong());
			}
		});
		register(java.sql.Timestamp.class, new Builder<java.sql.Timestamp>(){
			@Override
			public Class<java.sql.Timestamp> getType(){ return java.sql.Timestamp.class; }
			@Override
			public void writeTo(java.sql.Timestamp obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeLong(obj.getTime());
				}
			}
			@Override
			public java.sql.Timestamp parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return new java.sql.Timestamp(in.readLong());
			}
		});
		register(java.sql.Time.class, new Builder<java.sql.Time>(){
			@Override
			public Class<java.sql.Time> getType(){ return java.sql.Time.class; }
			@Override
			public void writeTo(java.sql.Time obj, GenericObjectOutput out) throws IOException
			{
				if( obj == null )
				{
					out.write0(OBJECT_NULL);
				}
				else
				{
					out.write0(OBJECT_VALUE);
					out.writeLong(obj.getTime());
				}
			}
			@Override
			public java.sql.Time parseFrom(GenericObjectInput in) throws IOException
			{
				byte b = in.read0();
				if( b == OBJECT_NULL )
					return null;
				if( b != OBJECT_VALUE )
					throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");

				return new java.sql.Time(in.readLong());
			}
		});
	}
}